/*
 * @Author: CBH37
 * @Date: 2023-01-26 17:27:11
 * @Description: `Nbd`的头文件
 */
#pragma once

#include <iostream>
#include <string>
#include <vector>
#include <memory>

#include "global.hpp"
#include "nlc_def.hpp"

class Nbd {
    class Value {
    public:
        virtual std::string codegen() = 0;
    };

    class Number : public Value {
        NlcFile::Num number;

    public:
        std::string codegen() override;
        Number(NlcFile::Num _number) : number(_number) {}
    };

    class String : public Value {
        std::string string;

    public:
        std::string codegen() override;
        String(std::string _string) : string(_string) {}
    };

    class LabelName : public Value {
        std::string name;
    
    public:
        std::string codegen() override;
        LabelName(std::string _name) : name(_name) {}
    };

    class Label : public Value {
        std::string name;
    
    public:
        std::string codegen() override;
        Label(std::string _name) : name(_name) {}

        inline std::string getName() { return name; }
    };

    // Instr = Mnemonic { Number | String | LabelName }*
    class Instr : public Value {
        std::string mnem;   // mnemonic 注记符
        std::vector<std::shared_ptr<Value>> args;
    
    public:
        std::string codegen() override;
        Instr(std::string _mnem, std::vector<std::shared_ptr<Value>> _args) : mnem(_mnem), args(_args) {}
    };

public:
    // Block = { Instr | Label }*
    class Block : public Value {
        std::string name;
        std::vector<std::shared_ptr<Value>> values;

    public:
        std::string codegen() override;
        Block(std::string _name) : name(_name) {}

        inline std::string getName() { return name; }
        void addValue(std::shared_ptr<Value> value) { values.push_back(value); }
    };

    // Block
    std::vector<std::shared_ptr<Block>> blocks;
    std::shared_ptr<Nbd::Block> newBlock(std::string blockName);

    std::shared_ptr<Block> beginBlock;
    void setBeginBlock(std::shared_ptr<Block> block);

    std::shared_ptr<Block> insertBlock; //指向当前被写入块
    void setInsertBlock(std::shared_ptr<Block> block);  // 设置被写入块

    std::string codegen();


    // newxxx
    std::shared_ptr<Nbd::Label> newLabel(std::string labelName);
    std::shared_ptr<Nbd::Instr> newInstr(std::string mnem, std::vector<std::shared_ptr<Value>> args);
    std::shared_ptr<Nbd::Number> newNumber(NlcFile::Num number);
    std::shared_ptr<Nbd::String> newString(std::string string);
    std::shared_ptr<Nbd::LabelName> newLabelName(std::string labelName);

    template<typename... T>
    void insertInstr(std::string mnem, T... args);


    // newInstrxxx
    void newInstrLoadLocal(std::string localName);
    void newInstrLoadGlobal(std::string globalName);
    void newInstrLoadNum(NlcFile::Num num);
    void newInstrLoadString(std::string string);
    void newInstrLoadAddr(std::shared_ptr<Block> block);
    void newInstrStoreLocal(std::string localName);
    void newInstrStoreGlobal(std::string globalName);

    void newInstrAdd();
    void newInstrSub();
    void newInstrMul();
    void newInstrDiv();
    void newInstrMod();
    void newInstrPow();

    void newInstrNot();
    void newInstrCompare(std::string op);

    void newInstrJmp(std::shared_ptr<Label> label);
    void newInstrJmpc(std::shared_ptr<Label> label);
    void newInstrCall();
    void newInstrCalle();
    void newInstrRet();

    void newInstrMakeList();
    void newInstrActionList(std::string actionName);
    void newInstrMakeMap();
    void newInstrActionMap(std::string actionName);

    void newInstrPopTop();
    void newInstrImport();
    void newInstrExit();
    void newInstrNop();
};